/*
 * Copyright (c) 2024, Qi Yaolong.
 * dragon@hbu.edu.cn
 * HeBei University, China.
 * 
 * 简体中文
 * 保留所有权利。除非符合（遵守）以下条款，否则您不得使用此文件：
 * 
 * 禁止任何未经授权的个人或组织以任何形式将本程序及其附属资料、创新、创意、架构设计、算法、衍生作品
 * 等用于任何商业目的、盈利活动、各类竞赛(比赛)、直播教学、录播教学、线下课程、书籍编写、教材编写、
 * 会议、培训、公益活动、项目课题、毕业设计、毕业论文、学术论文等。
 * 
 * 本程序由版权持有人和贡献人“按原样”提供，不提供任何明示或暗示担保，包括但不局限于对适销性和特定
 * 用途适合性的暗示担保。在任何情况下，版权持有人或贡献人对因使用本程序而导致的任何直接、间接、附带、
 * 特殊、典型或后果性损害（包括但不限于购买替代商品或服务；使用损失、数据丢失或利润损失；业务中断）
 * 不承担任何责任，无论其如何产生，也不论其责任理论为何，无论是合同、严格责任还是侵权（包括疏忽或
 * 其他），即使已告知此类损害的可能性。
 * 
 * English
 * All rights reserved. You may NOT use this file except in compliance with the
 * following terms:
 * 
 * IT IS PROHIBITED FOR ANY UNAUTHORIZED PERSON OR ENTITY TO USE THIS PROGRAM AND 
 * ITS ANCILLARY DATA, INNOVATIONS, CONCEPTS, ARCHITECTURE DESIGN, ALGORITHMS, 
 * DERIVED WORKS, ETC., IN ANY FORM FOR COMMERCIAL PURPOSES, PROFIT-MAKING 
 * ACTIVITIES, VARIOUS COMPETITIONS (CONTESTS), LIVE TEACHING, PRE-RECORDED 
 * LESSONS, OFFLINE COURSES, BOOK WRITING, CONFERENCES, TRAININGS, PUBLIC WELFARE 
 * ACTIVITIES, PROJECTS, TOPICS, GRADUATION DESIGNS, DISSERTATIONS, THESES, AND 
 * OTHER APPLICATIONS.
 * 
 * THIS PROGRAM IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS PROGRAM, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "string.h"
#include "common_def.h"
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "osal_debug.h"
#include "osal_task.h"
#include "securec.h"
#include "sle_device_discovery.h"
#include "sle_connection_manager.h"
#include "sle_ssap_client.h"
#include "sle_errcode.h"
#include "ohos_sle_common.h"
#include "ohos_sle_errcode.h"
#include "ohos_sle_ssap_server.h"
#include "ohos_sle_ssap_client.h"
#include "ohos_sle_device_discovery.h"
#include "ohos_sle_connection_manager.h"
#include "pinctrl.h"
#include "errcode.h"
#include "global_settings.h" 
#include "sle_client.h"
#include "ssd1306.h"     
#include "trng.h"        
#include "easy_cipher.h" 

uint8_t aes_key[16] = {0x36, 0x51, 0x82, 0x17, 0x96, 0x70, 0xac, 0x8e,
                       0x2f, 0xbd, 0xea, 0xcf, 0x9e, 0xca, 0x8d, 0xf0};

uint8_t aes_iv[16] = {0};

#define SLE_MTU_SIZE_DEFAULT 512 

#define SLE_SEEK_INTERVAL_DEFAULT 100 

#define SLE_SEEK_WINDOW_DEFAULT 100 

#define SLE_TASK_DELAY_MS 1000        
#define SLE_CLIENT_LOG "[sle client]" 

static char g_sle_uuid_app_uuid[] = {0x39, 0xBE, 0xA8, 0x80, 0xFC, 0x70, 0x11, 0xEA,
                                     0xB7, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

static ssapc_find_service_result_t g_sle_find_service_result = {0};

static sle_announce_seek_callbacks_t g_sle_seek_cbk = {0};

static SleConnectionCallbacks g_sle_connect_cbk = {0};

static ssapc_callbacks_t g_sle_ssapc_cbk = {0};

static SleAddr g_sle_remote_addr = {0};

ssapc_write_param_t g_sle_send_param = {0};

uint8_t g_sle_connected = 0;

uint8_t g_sle_service_found = 0;

uint16_t g_sle_conn_id = 0;

uint8_t g_client_id = 0;

char g_sle_server_name[128] = "";

char g_sle_server_token[128] = "";

void sle_set_server_name(char *name)
{
    memcpy_s(g_sle_server_name, strlen(name), name, strlen(name));
}

void sle_set_server_token(char *token)
{
    memcpy_s(g_sle_server_token, strlen(token), token, strlen(token));
}

uint8_t sle_is_connected(void)
{
    return g_sle_connected;
}

uint16_t sle_get_conn_id(void)
{
    return g_sle_conn_id;
}

ssapc_write_param_t *sle_get_send_param(void)
{
    return &g_sle_send_param;
}

void sle_start_scan(void)
{
    
    SleSeekParam param = {0};

    param.ownaddrtype = SLE_ADDRESS_TYPE_PUBLIC;

    param.filterduplicates = 0;

    param.seekfilterpolicy = SLE_SEEK_FILTER_ALLOW_ALL;

    param.seekphys = SLE_SEEK_PHY_1M;

    param.seekType[0] = SLE_SEEK_ACTIVE;

    param.seekInterval[0] = SLE_SEEK_INTERVAL_DEFAULT;

    param.seekWindow[0] = SLE_SEEK_WINDOW_DEFAULT;

    SleSetSeekParam(&param);

    SleStartSeek();
}

static void sle_client_sle_enable_cbk(errcode_t status)
{
    if (status != 0)
    {
        osal_printk("%s [sle_client_sle_enable_cbk] status error\r\n", SLE_CLIENT_LOG);
    }
    else
    {
        osal_msleep(SLE_TASK_DELAY_MS);

        sle_start_scan();
    }
}

static void sle_client_seek_enable_cbk(errcode_t status)
{
    if (status != 0)
    {
        osal_printk("%s [sle_client_seek_enable_cbk] status error\r\n", SLE_CLIENT_LOG);
    }
}

static void sle_client_seek_result_info_cbk(SleSeekResultInfo *seek_result_data)
{
    errcode_t ret;

    if (seek_result_data == NULL)
    {
        osal_printk("%s [sle_client_seek_result_info_cbk] status error\r\n", SLE_CLIENT_LOG);
        return;
    }

#if MODE_DEBUG
    
    printf("-------------------------------------------------------------\r\n");
    osal_printk("%s [sle_client_seek_result_info_cbk] eventType:%d\r\n", SLE_CLIENT_LOG, seek_result_data->eventType);
    osal_printk("%s [sle_client_seek_result_info_cbk] addr:%02x:%02x:%02x:%02x:%02x:%02x\r\n", SLE_CLIENT_LOG,
                seek_result_data->addr.addr[0], seek_result_data->addr.addr[1], seek_result_data->addr.addr[2],
                seek_result_data->addr.addr[3], seek_result_data->addr.addr[4], seek_result_data->addr.addr[5]);
    osal_printk("%s [sle_client_seek_result_info_cbk] directAddr:%02x:%02x:%02x:%02x:%02x:%02x\r\n", SLE_CLIENT_LOG,
                seek_result_data->directAddr.addr[0], seek_result_data->directAddr.addr[1], seek_result_data->directAddr.addr[2],
                seek_result_data->directAddr.addr[3], seek_result_data->directAddr.addr[4], seek_result_data->directAddr.addr[5]);
    osal_printk("%s [sle_client_seek_result_info_cbk] rssi:%d\r\n", SLE_CLIENT_LOG, seek_result_data->rssi);
    osal_printk("%s [sle_client_seek_result_info_cbk] dataStatus:%d\r\n", SLE_CLIENT_LOG, seek_result_data->dataStatus);
    osal_printk("%s [sle_client_seek_result_info_cbk] dataLength:%d\r\n", SLE_CLIENT_LOG, seek_result_data->dataLength);
    osal_printk("%s [sle_client_seek_result_info_cbk] data:%s\r\n", SLE_CLIENT_LOG, seek_result_data->data);
    
#if MODE_TRUSTED
    for (int i = 0; i < seek_result_data->dataLength; i++)
    {
        osal_printk("%s [sle_client_seek_result_info_cbk] data[%d]:0x%02x \r\n", SLE_CLIENT_LOG, i, seek_result_data->data[i]);
    }
#else
    for (int i = 0; i < seek_result_data->dataLength; i++)
    {
        osal_printk("%s [sle_client_seek_result_info_cbk] data[%d]:0x%02x\t\t%c\r\n", SLE_CLIENT_LOG, i, seek_result_data->data[i], seek_result_data->data[i]);
    }
#endif
    printf("-------------------------------------------------------------\r\n");
#endif

    if (seek_result_data->eventType == 2)
    {
        
        uint16_t len = seek_result_data->data[6];
        if (len < 1) 
        {
            return;
        }

        uint8_t type = seek_result_data->data[7];
        if (type != 0xff) 
        {
            return;
        }

        char token[251] = {0};            
        for (int i = 0; i < len - 1; i++) 
        {
            token[i] = seek_result_data->data[8 + i];
        }
        
#if MODE_TRUSTED
        
        memcpy_s(aes_iv, 16, token, 16);

        uint16_t encrypted_data_len = len - 1 - 16;                                       
        uint8_t encrypted_data[241] = {0};                                                
        memcpy_s(encrypted_data, sizeof(encrypted_data), token + 16, encrypted_data_len); 

        CipherAesSetIv(aes_iv);

        uint8_t decrypted_data[241] = {0};                                          
        uint16_t decrypted_data_len = 0;                                            
        ret = CipherAesDecrypt(encrypted_data, decrypted_data, encrypted_data_len); 
        if (ret != 0)
        {
            osal_printk("%s [sle_client_seek_result_info_cbk] CipherAesDecrypt error = %x\r\n", SLE_CLIENT_LOG, ret);
            return;
        }
        decrypted_data[encrypted_data_len] = '\0'; 
        printf("decrypted_data: %s\r\n", decrypted_data);

        if (strstr((const char *)decrypted_data, g_sle_server_token) != NULL) 
        {
            printf("starBeacon found! rssi: %d\r\n", seek_result_data->rssi);
            ssd1306_SetCursor(0, 20);
            ssd1306_ClearLine(Font_7x10, Black);
            ssd1306_printf(Font_7x10, White, "found! rssi: %d\r\n", seek_result_data->rssi);

        }
#else
        
        if (strstr((const char *)token, g_sle_server_token) != NULL) 
        {
            printf("starBeacon found! rssi: %d\r\n", seek_result_data->rssi);
            ssd1306_SetCursor(0, 20);
            ssd1306_ClearLine(Font_7x10, Black);
            ssd1306_printf(Font_7x10, White, "found! rssi: %d\r\n", seek_result_data->rssi);

        }
#endif
    }
}

static void sle_client_seek_disable_cbk(errcode_t status)
{
    if (status != 0)
    {
        osal_printk("%s [sle_client_seek_disable_cbk] status error = %x\r\n", SLE_CLIENT_LOG, status);
    }
    else
    {
        
    }
}

static void sle_client_seek_cbk_register(void)
{
    g_sle_seek_cbk.sle_enable_cb = sle_client_sle_enable_cbk;        
    g_sle_seek_cbk.seek_enable_cb = sle_client_seek_enable_cbk;      
    g_sle_seek_cbk.seek_result_cb = sle_client_seek_result_info_cbk; 
    g_sle_seek_cbk.seek_disable_cb = sle_client_seek_disable_cbk;    
    sle_announce_seek_register_callbacks(&g_sle_seek_cbk);           
}

static errcode_t sle_uuid_client_register(void)
{
    errcode_t ret;

    SleUuid app_uuid = {0};
    printf("[uuid client] ssapc_register_client \r\n");
    app_uuid.len = sizeof(g_sle_uuid_app_uuid);
    if (memcpy_s(app_uuid.uuid, app_uuid.len, g_sle_uuid_app_uuid, sizeof(g_sle_uuid_app_uuid)) != EOK)
    {
        return ERRCODE_SLE_FAIL;
    }

    ret = SsapcRegisterClient(&app_uuid, &g_client_id);

    return ret;
}

void sle_client_init()
{
    
    uint8_t local_addr[SLE_ADDR_LEN] = {0x13, 0x67, 0x5c, 0x07, 0x00, 0x51}; 
    random_mac_addr(local_addr);                                             
    SleAddr local_address;

    local_address.type = SLE_ADDRESS_TYPE_PUBLIC;

    (void)memcpy_s(local_address.addr, SLE_ADDR_LEN, local_addr, SLE_ADDR_LEN);

    CipherAesSetKey(aes_key);

    sle_uuid_client_register();

    sle_client_seek_cbk_register();

    EnableSle();

    SleSetLocalAddr(&local_address);
}
